import type { BrowserJsPlumbDefaults, BrowserJsPlumbInstance } from '@jsplumb/browser-ui'
import { type Component, computed, onMounted, type Ref, ref, watch } from 'vue'
import { Code, CycleOne, Incoming, Outgoing } from '@icon-park/vue-next'
import { onKeyDown, onKeyUp, useElementSize, useMagicKeys } from '@vueuse/core'
import { useDataFlowState } from '@/model/data-flow/data-flow-state'

export const operatorTypeInfoMapping: Map<
  string,
  {
    icon: Component
    name: string
  }
> = new Map([
  [
    'source',
    {
      icon: Incoming,
      name: '输入算子'
    }
  ],
  [
    'process',
    {
      icon: CycleOne,
      name: '处理算子'
    }
  ],
  [
    'sql',
    {
      icon: Code,
      name: 'SQL算子'
    }
  ],
  [
    'sink',
    {
      icon: Outgoing,
      name: '输出算子'
    }
  ]
])

//jsPlump配置
export function generateBrowserJsPlumbDefaults(element: Element): BrowserJsPlumbDefaults {
  return {
    container: element,
    connectionOverlays: [
      {
        type: 'Arrow',
        options: {
          location: 1,
          width: 7,
          length: 10
        }
      }
    ],
    anchor: 'Continuous',
    elementsDraggable: true,
    endpoint: 'Blank',
    connector: {
      type: 'Bezier',
      options: {
        curviness: 50
      }
    },
    paintStyle: {
      strokeWidth: 1,
      stroke: '#606266',
      outlineWidth: 10,
      outlineStroke: 'transparent'
    }
  }
}

export function initJsplumbInstance(jsplumbInstance: BrowserJsPlumbInstance) {
  jsplumbInstance.addSourceSelector('.node-placeholder', {
    anchor: 'Continuous',
    allowLoopback: false
  })

  jsplumbInstance.addTargetSelector('.function-node-4-operation-space', {
    anchor: 'Continuous',
    allowLoopback: false
  })
}

//键盘快捷键
export function useKeyboardAction() {
  const keys = useMagicKeys({
    passive: false,
    onEventFired(e) {
      if (e.ctrlKey && e.key === 'a') e.preventDefault()
    }
  })
  const ctrlA = keys['Ctrl+A']
  const ctrlC = keys['Ctrl+C']
  const ctrlV = keys['Ctrl+V']

  return { ctrlA, ctrlC, ctrlV }
}

//画布拖拽
export function useOperationSpaceDrag(
  operationSpaceWrapperRef: Ref<any>,
  operationSpaceScrollbarRef: Ref<any>
) {
  const operationSpaceVisibleAreaSize = useElementSize(operationSpaceWrapperRef)
  const operationSpaceZoom = ref(1.0)
  const operationSpacePosition = ref({
    left: 500,
    top: 500
  })

  const moveStart = ref({
    left: 0,
    top: 0,
    x: 0,
    y: 0
  })
  const mousedown = ref(false)
  const spaceKeyDown = ref(false)
  const inMoving = computed(() => {
    return mousedown.value && spaceKeyDown.value
  })
  onKeyDown(
    ' ',
    (e) => {
      e.preventDefault()
      spaceKeyDown.value = true
    },
    { dedupe: false }
  )
  onKeyUp(
    ' ',
    (e) => {
      e.preventDefault()
      spaceKeyDown.value = false
    },
    { dedupe: false }
  )

  function startMove(event: MouseEvent) {
    mousedown.value = true
    moveStart.value.left = operationSpacePosition.value.left
    moveStart.value.top = operationSpacePosition.value.top
    moveStart.value.x = event.x
    moveStart.value.y = event.y
  }

  function move(event: MouseEvent) {
    if (!inMoving.value) {
      return
    }
    operationSpacePosition.value.left = Math.max(
      0,
      Math.min(
        moveStart.value.left - (event.x - moveStart.value.x),
        2000 * operationSpaceZoom.value - operationSpaceVisibleAreaSize.width.value
      )
    )
    operationSpacePosition.value.top = Math.max(
      0,
      Math.min(
        moveStart.value.top - (event.y - moveStart.value.y),
        2000 * operationSpaceZoom.value - operationSpaceVisibleAreaSize.height.value
      )
    )
  }

  function endMove() {
    mousedown.value = false
  }

  //更新画布位置
  watch(operationSpacePosition.value, () => {
    operationSpaceScrollbarRef.value.scrollTo(
      operationSpacePosition.value.left,
      operationSpacePosition.value.top
    )
  })

  function scroll(e: Event) {
    operationSpacePosition.value.left = (e as any).scrollLeft
    operationSpacePosition.value.top = (e as any).scrollTop
  }

  onMounted(() => {
    operationSpaceScrollbarRef.value.scrollTo(
      operationSpacePosition.value.left,
      operationSpacePosition.value.top
    )
  })

  return { operationSpacePosition, inMoving, operationSpaceZoom, startMove, move, endMove, scroll }
}

export function useSelectArea(
  selectAreaRef: Ref<any>,
  jsplumbInstance: Ref<BrowserJsPlumbInstance | undefined>
) {
  const showSelectArea = ref(false)

  const { dataFlowState } = useDataFlowState()

  const bindSelectArea = () => {
    const endpoint = jsplumbInstance.value?.addEndpoint(
      selectAreaRef.value,
      {
        source: false,
        target: false,
        maxConnections: 0
      },
      {
        uuid: selectAreaRef.value.id
      }
    )
    jsplumbInstance.value?.addToDragSelection(endpoint?.element)
  }

  const setSelectAreaPosition = (selectAreaPosition: any) => {
    selectAreaRef.value.style.left = selectAreaPosition.left + 'px'
    selectAreaRef.value.style.top = selectAreaPosition.top + 'px'
    selectAreaRef.value.style.width = selectAreaPosition.width - selectAreaPosition.left + 'px'
    selectAreaRef.value.style.height = selectAreaPosition.height - selectAreaPosition.top + 'px'
  }

  watch(dataFlowState.selectedNodes, () => {
    if (dataFlowState.selectedNodes.size <= 1) {
      showSelectArea.value = false
      jsplumbInstance.value?.clearDragSelection()
      return
    }
    jsplumbInstance.value?.clearDragSelection()
    const selectAreaPosition = {
      left: Number.MAX_VALUE,
      top: Number.MAX_VALUE,
      width: 0,
      height: 0
    }
    for (const id of dataFlowState.selectedNodes) {
      const element = document.getElementById(id)
      if (!element) {
        continue
      }
      const left = Number.parseInt(element.style.left.replace('px', ''))
      const top = Number.parseInt(element.style.top.replace('px', ''))
      const rect = element.getBoundingClientRect()
      selectAreaPosition.left = Math.min(selectAreaPosition.left, left)
      selectAreaPosition.top = Math.min(selectAreaPosition.top, top)
      selectAreaPosition.width = Math.max(selectAreaPosition.width, left + rect.width)
      selectAreaPosition.height = Math.max(selectAreaPosition.height, top + rect.height)
      jsplumbInstance.value?.addToDragSelection(jsplumbInstance.value?.getEndpoint(id).element)
    }

    jsplumbInstance.value?.addToDragSelection(
      jsplumbInstance.value?.getEndpoint(selectAreaRef.value.id).element
    )
    setSelectAreaPosition(selectAreaPosition)
    showSelectArea.value = true
  })

  return { showSelectArea, bindSelectArea }
}
